﻿using System;
using System.Linq;
using innovations.util.exts.mathdotnet;
using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.LinearAlgebra.Generic;
using System.Collections.Generic;

namespace innovations.ml.core
{
    public class Prediction
    {
        public static double Predict(Vector<double> newX, Vector<double> theta)
        {
            return newX.DotProduct(theta);
        }

        public static Vector<double> Predict(Matrix<double> newX, Vector<double> theta)
        {
            Predictions = new DenseVector(newX.RowCount);
            Vector<double> sigmoidValue = Sigmoid.Compute((newX).Multiply(theta));
            for (int i = 0; i < Predictions.Count; i++)
            {
                if (sigmoidValue[i] >= 0.5)
                    Predictions[i] = 1;
                else
                    Predictions[i] = 0;
            }

            return Predictions;
        }

        public static Vector<double> Predict(Matrix<double> newX, Matrix<double> theta)
        {
            Predictions = new DenseVector(newX.RowCount);
            Matrix<double> predictionsMatrix = Sigmoid.Compute(theta * newX.Transpose()).Transpose();

            for (int i = 0; i < predictionsMatrix.RowCount; i++)
                Predictions[i] = predictionsMatrix.Row(i).MaximumIndex();

            return Predictions;
        }

        public static Vector<double> Predict(Matrix<double> newX, List<Matrix<double>> thetaList)
        {
            Predictions = new DenseVector(newX.RowCount);
            Matrix<double> predictionsMatrix = new DenseMatrix(newX);
            foreach (var theta in thetaList)
            {
                predictionsMatrix = Sigmoid.Compute(theta * predictionsMatrix.Transpose()).Transpose();
                if (thetaList.Last() != theta)
                    predictionsMatrix = predictionsMatrix.InsertColumn(0, new DenseVector(predictionsMatrix.RowCount, 1.0));
            }
            for (int i = 0; i < predictionsMatrix.RowCount; i++)
                Predictions[i] = predictionsMatrix.Row(i).MaximumIndex();
            return Predictions;
        }

        public static double ComputeTrainingAccuracy(Vector<double> Y)
        {
            if (Predictions == null || Predictions.Count == 0)
                throw new ArgumentNullException("You must run Predict before getting training accuracy.");
            return Predictions.Count - Predictions.Subtract(Y).PointwisePow(2).Sum();
        }

        public static double ComputeMean(Vector<double> Y)
        {
            return Predictions.PointwiseEqual(Y).Average();
        }        

        public static Vector<double> Predictions { get; private set; }
    }
}